package com.zyf.tts.service;

import cn.hutool.core.io.FileUtil;
import com.zyf.service.audio.HandleAudioService;
import com.zyf.tts.TTS;
import com.zyf.tts.TTSVoice;
import com.zyf.tts.bean.Voice;
import com.zyf.utils.FFmpegUtils;
import com.zyf.utils.HttpClientUtils;
import com.zyf.utils.ListUtil;
import com.zyf.utils.StringUtil;

import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 合成语音
 */
public class HandleTTSService {

    private static final int MAXLENGTH = 2620;//GSV单次处理最大文本内容长度

    private static final double RATE = 0.002;

    private static final String TTSURL = "http://192.168.1.2:9880";

//    private static final String REFPATH = "./参考音频/海晓有声书.wav";
//
//    private static final String REFTEXT = "一个初秋的上午，玉泰茶馆开始营业，掌柜王立发兴致勃勃地坐在柜台上。";


    //妍妍
    private static final String REFPATH = "./参考音频/妍.wav";

    private static final String REFTEXT = "华而不实的怪鸟,一只怪鸟从远方飞来。";
//    private static final int MAXLENGTH = 1180;

    /**
     * 调用GSV接口 9880
     *
     * @param txtFile
     * @return
     * @throws IOException
     */
    public String handleSingleTTS(String url, String refPath, String refText, String txtFile) throws Exception {
        File file = new File(txtFile);
        //读取源文件
        String srcContent = getSrcTxtByLine(txtFile);
        System.out.println("字符长度：" + srcContent.length());
        //设置请求参数
        StringBuffer param = new StringBuffer();
        param.append("text=" + URLEncoder.encode(srcContent, "utf-8"));
        param.append("&text_lang=zh&");
        param.append("ref_audio_path=" + URLEncoder.encode(refPath, "utf-8"));
        param.append("&prompt_lang=zh&");
        param.append("prompt_text=" + URLEncoder.encode(refText, "utf-8"));
        param.append("&text_split_method=cut5&batch_size=20&media_type=wav&speed_factor=1.0");

        HttpClientUtils.doGet(url, param.toString());
        //新文件夹路径
        String wavPath = "F:\\project\\GPT_SOVTTS\\GPT-SoVITS-v2\\音频输出\\audio.wav";
        String srtPath = "F:\\project\\GPT_SOVTTS\\GPT-SoVITS-v2\\音频输出\\tts-out.srt";
        //复制文件
        copyFileByResetName(wavPath, file.getParent(),file.getName().replace("txt","wav"));
        copyFileByResetName(srtPath, file.getParent(),file.getName().replace("txt","srt"));

        return file.getParent() + File.separator + file.getName();
    }


    /**
     * 根据txt合成语音
     */
    public void handleBatchTTSByTXT(String txtpath) throws Exception {
        //生成文件夹
        File txtfile = new File(txtpath);
        if (!txtfile.exists()) {
            throw new RuntimeException("文本不存在");
        }
        String filename = txtfile.getName().substring(0, txtfile.getName().indexOf("."));
        File foleder = new File(txtfile.getParent() + File.separator + filename);
        if (!foleder.exists()) {
            foleder.mkdir();
        }
        //解析文本
        String content = getSrcTxtByLine(txtpath);
        int total = content.length();
        int circle = total / MAXLENGTH + 1;
        String[] punctuation = {"。", "？", "！"};
        List<String> punctuations = Arrays.asList(punctuation);
        List<String> conlist = new ArrayList<>();
        for (int i = 0; i < circle; i++) {
            String res = "";
            //判断此句有几个停顿
            int flag = 0;
            if (content.length() > MAXLENGTH) {
                int endindex = MAXLENGTH + 100;
                if (content.length() <= MAXLENGTH + 100) {
                    endindex = content.length();
                }
                String match = content.substring(MAXLENGTH, endindex);
                for (char st : match.toCharArray()) {
                    String puncStr = st + "";
                    if (punctuations.contains(puncStr)) {
                        break;
                    } else {
                        flag++;
                    }
                }
                if (flag > 0) {
                    res = content.substring(0, MAXLENGTH + flag + 1);
                } else {
                    res = content.substring(0, MAXLENGTH + 1);
                }
                content = content.substring(res.length());
            } else {
                res = content;
                content = content.substring(res.length());
            }
            if (!StringUtil.isBlank(res)) {
                conlist.add(res);
                String finalsrt = foleder + File.separator + (i + 1) + "_part.txt";
                writeFile(res, finalsrt);
            }
        }

    }

    /**
     * 写入文件
     *
     * @param buffer
     * @param finalsrt
     * @return
     * @throws Exception
     */
    private String writeFile(String buffer, String finalsrt) throws Exception {
        //写入文件
        FileWriter writer = null;
        try {
            File file = new File(finalsrt);
            if (file.exists()) {
                file.delete();
            }
            writer = new FileWriter(finalsrt, false);
            writer.write(buffer);
            return finalsrt;
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (writer != null) {
                writer.flush();
                writer.close();
            }
        }
    }


    /**
     * 读取源文件
     */
    public String getSrcTxtByLine(String filepath) throws FileNotFoundException {
        String[] punctuation = {"。", "，", "、", "；", "：", "？", "！", "•", ".", "—"};
        List<String> punctuations = Arrays.asList(punctuation);
        StringBuffer buffer = new StringBuffer();
        InputStream inputstream = new FileInputStream(new File(filepath));
        BufferedReader reader = null;
        String temp = "";
        try {
            int index = 0;
            reader = new BufferedReader(new InputStreamReader(inputstream));
            while ((temp = reader.readLine()) != null) {
                if (index == 1) {
                    for (int i = 0; i < temp.toCharArray().length; i++) {
                        String currStr = temp.toCharArray()[i] + "";
                        if (i > 1) {
                            if (!isChinese(currStr) && !punctuations.contains(currStr)) {
                                buffer.append("，");
                            } else {
                                buffer.append(currStr);
                            }
                        } else {
                            if (isChinese(currStr) || punctuations.contains(currStr)) {
                                buffer.append(currStr);
                            }
                        }
                    }
                    buffer.append("。");
                } else {
                    for (char strChar : temp.toCharArray()) {
                        String currStr = strChar + "";
                        if (isChinese(currStr) || punctuations.contains(currStr)) {
                            buffer.append(currStr);
                        }
                    }
                }
                index++;
            }
        } catch (IOException e) {
            if (reader != null)
                try {
                    reader.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return buffer.toString();
    }


    public boolean isChinese(String str) {
        boolean isChinese = true;
        for (char c : str.toCharArray()) {
            if (!Character.isIdeographic(c)) {
                isChinese = false;
                break;
            }
        }
        return isChinese;
    }

    /**
     * 批量生成语音
     *
     * @param folderPath
     * @throws Exception
     */
    public void handleBatchTTS(String folderPath) throws Exception {
        //先批量生成配音文件
        List<File> txtFiles = FileUtil.loopFiles(folderPath, backfile -> backfile.isFile() && backfile.getName().endsWith(".txt"));
        for (File file : txtFiles) {
            try {
                handleTTSByLimit(file.getAbsolutePath());
            } catch (Exception e) {
                System.out.println(e.toString());
            }
        }
    }

    /**
     * begin 根据txt的长度限制合成语音
     */
    public String handleTTSByLimit(String txtpath) throws Exception {
        //生成文件夹
        File txtfile = new File(txtpath);
        if (!txtfile.exists()) {
            throw new RuntimeException("文本不存在");
        }
        //解析文本
        String content = getSrcTxtByLine(txtpath);
        //判断文本的长度,如果文本长度小于GSV允许最大长度，则直接用单个tts;否则拆分合成
        int lengthTxt = content.length();
        if (lengthTxt < MAXLENGTH) {
            String audioPath = handleSingleTTS(TTSURL, REFPATH, REFTEXT, txtpath);
            return audioPath;
        }
        String filename = txtfile.getName().substring(0, txtfile.getName().indexOf("."));
        File foleder = new File(txtfile.getParent() + File.separator + filename);
        if (!foleder.exists()) {
            foleder.mkdir();
        }
        int total = content.length();
        int circle = total / MAXLENGTH + 1;
        String[] punctuation = {"。", "？", "！"};
        List<String> punctuations = Arrays.asList(punctuation);
        List<String> conlist = new ArrayList<>();
        for (int i = 0; i < circle; i++) {
            String res = "";
            //判断此句有几个停顿
            int flag = 0;
            if (content.length() > MAXLENGTH) {
                int endindex = MAXLENGTH + 100;
                if (content.length() <= MAXLENGTH + 100) {
                    endindex = content.length();
                }
                String match = content.substring(MAXLENGTH, endindex);
                for (char st : match.toCharArray()) {
                    String puncStr = st + "";
                    if (punctuations.contains(puncStr)) {
                        break;
                    } else {
                        flag++;
                    }
                }
                if (flag > 0) {
                    res = content.substring(0, MAXLENGTH + flag + 1);
                } else {
                    res = content.substring(0, MAXLENGTH + 1);
                }
                content = content.substring(res.length());
            } else {
                res = content;
                content = content.substring(res.length());
            }
            if (!StringUtil.isBlank(res)) {
                conlist.add(res);
            }
        }
        //新文件夹路径
        String wavPath = "F:\\project\\GPT_SOVTTS\\GPT-SoVITS-v2\\音频输出\\audio.wav";
        String srtPath = "F:\\project\\GPT_SOVTTS\\GPT-SoVITS-v2\\音频输出\\tts-out.srt";
        //遍历生成语音合成文件
        for (int i = 0; i < conlist.size(); i++) {
            handleSingleTTSByContent(TTSURL, REFPATH, REFTEXT, conlist.get(i));
            //复制文件
            copyFileByName(wavPath, foleder.getAbsolutePath(), "audio" + i + ".wav");
            copyFileByName(srtPath, foleder.getAbsolutePath(), "tts-out" + i + ".srt");
        }
        //合并多个音频
        String finalAudio = mergeMulAudiosByFFmpeg(foleder.getAbsolutePath());
        //合并srt
        String finalSrt = mergeMulSrts(foleder.getAbsolutePath());
        //复制到父级文件夹
        copyFileByName(finalAudio, txtfile.getParent(),filename+".wav");
        //复制到父级文件夹
        copyFileByName(finalSrt, txtfile.getParent(),filename+".srt");
        //删除音频合并记录表
        File audioTxt = new File(txtfile.getParent() + File.separator + "audio.txt");
        if (audioTxt != null) {
            audioTxt.delete();
        }
        //删除拆分后的文件
        List<File> audioList = FileUtil.loopFiles(foleder.getAbsolutePath(), audiofile -> audiofile.isFile());
        for (File file : audioList) {
            if (file != null) {
                file.delete();
            }
        }
        //删除文件夹
        foleder.delete();
        return txtfile.getParent() + File.separator + new File(finalAudio).getName();
    }

    public void copyFileByName(String sourceFile, String desfolder, String fileName) throws IOException {
        // 源文件路径
        Path source = Paths.get(sourceFile);
        // 目标文件夹路径
        Path destinationDir = Paths.get(desfolder);
        // 目标文件路径
        Path destination = destinationDir.resolve(fileName);
        // 确保目标文件夹存在
        Files.createDirectories(destinationDir);
        // 使用Files.copy()方法复制文件到目标文件夹
        Files.copy(source, destination);
    }

    public void copyFile(String sourceFile, String desfolder) throws IOException {
        // 源文件路径
        Path source = Paths.get(sourceFile);
        // 目标文件夹路径
        Path destinationDir = Paths.get(desfolder);
        // 目标文件路径
        Path destination = destinationDir.resolve(source.getFileName());
        // 确保目标文件夹存在
        Files.createDirectories(destinationDir);
        // 使用Files.copy()方法复制文件到目标文件夹
        Files.copy(source, destination);
    }

    public void copyFileByResetName(String sourceFile, String desfolder,String filename) throws IOException {
        // 源文件路径
        Path source = Paths.get(sourceFile);
        // 目标文件夹路径
        Path destinationDir = Paths.get(desfolder);
        // 目标文件路径
        Path destination = destinationDir.resolve(filename);
        // 确保目标文件夹存在
        Files.createDirectories(destinationDir);
        // 使用Files.copy()方法复制文件到目标文件夹
        Files.copy(source, destination);
    }

    /**
     * 调用GSV接口 9880
     *
     * @return
     * @throws IOException
     */
    public void handleSingleTTSByContent(String url, String refPath, String refText, String srcContent) throws Exception {
        //设置请求参数
        StringBuffer param = new StringBuffer();
        param.append("text=" + URLEncoder.encode(srcContent, "utf-8"));
        param.append("&text_lang=zh&");
        param.append("ref_audio_path=" + URLEncoder.encode(refPath, "utf-8"));
        param.append("&prompt_lang=zh&");
        param.append("prompt_text=" + URLEncoder.encode(refText, "utf-8"));
        param.append("&text_split_method=cut5&batch_size=20&media_type=wav&speed_factor=1.0");

        HttpClientUtils.doGet(url, param.toString());
        //新文件夹路径
        String wavPath = "F:\\project\\GPT_SOVTTS\\GPT-SoVITS-v2\\音频输出\\audio.wav";
        String srtPath = "F:\\project\\GPT_SOVTTS\\GPT-SoVITS-v2\\音频输出\\tts-out.srt";
        //复制文件
//        copyFile(wavPath, file.getParent());
//        copyFile(srtPath, file.getParent());
    }

    /**
     * 合并多个音频  ffmpeg -f concat -safe 0 -i filelist.txt -c copy output.mp3
     */
    public String mergeMulAudiosByFFmpeg(String audiopath) throws Exception {
        List<File> totalFiles = FileUtil.loopFiles(audiopath, file -> file.isFile() && (file.getName().endsWith(".wav") || file.getName().endsWith(".mp3")));
        List<File> audioFiles = new ArrayList<>();
        for (File file : totalFiles) {
            //转换格式为wav
            if (file.getName().endsWith(".mp3")) {
                String newpath = new HandleAudioService().converAudio(file.getAbsolutePath());
                audioFiles.add(new File(newpath));
            }
            if (file.getName().endsWith(".wav")) {
                audioFiles.add(file);
            }
        }
        audioFiles.sort(new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });
        StringBuffer buffer = new StringBuffer();
        for (File file : audioFiles) {
            buffer.append("file");
            buffer.append(" ");
            buffer.append("'");
            buffer.append(file.getAbsolutePath());
            buffer.append("'");
            buffer.append("\n");
        }
        //保存txt
        String txtpath = audiopath + File.separator + "audio.txt";
        FileWriter writer = null;
        try {
            writer = new FileWriter(txtpath, false);
            writer.write(buffer.toString());
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (writer != null) {
                writer.flush();
                writer.close();
            }
        }
        String finalPath = audiopath + File.separator + "final.wav";
        List<String> commands = new ArrayList<>();
        commands.add("ffmpeg");
        commands.add("-f");
        commands.add("concat");
        commands.add("-safe");
        commands.add("0");
        commands.add("-i");
        commands.add(txtpath);
        commands.add("-c");
        commands.add("copy");
        commands.add(finalPath);

        String result = FFmpegUtils.execFFmpegCommand(commands);
        System.out.println(result);

        return finalPath;
    }

    /**
     * 根据顺序合并多个srt文件
     *
     * @param absolutePath
     */
    public String mergeMulSrts(String absolutePath) throws Exception {
        List<File> srtList = FileUtil.loopFiles(absolutePath, audiofile -> audiofile.isFile() && audiofile.getName().endsWith(".srt"));
        srtList.sort(new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });
        StringBuffer buffer = new StringBuffer();
        String lastTimeVersion = "";
        int lastIndex = 0;
        FileWriter writer = null;
        try {
            for (int i = 0; i < srtList.size() - 1; i++) {
                if (i == 0) {
                    String first = srtList.get(i).getAbsolutePath();
                    Map res = getSrtFileByLine(first, buffer);
                    lastIndex = (int) res.get("lastIndex");
                    lastTimeVersion = (String) res.get("lastTimeVersion");
                }
                String currentPath = srtList.get(i + 1).getAbsolutePath();
                //读取第一个文件并提取最后的时间
                Map<String, Object> curRes = getBalSrtFileByLine(currentPath, buffer, lastIndex, lastTimeVersion);
                lastIndex = (int) curRes.get("lastIndex");
                lastTimeVersion = (String) curRes.get("lastTimeVersion");
            }
            //写入文件
            String finalpath = absolutePath + File.separator + "final.srt";
            writer = new FileWriter(finalpath, false);
            writer.write(buffer.toString());
            return finalpath;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (writer != null) {
                writer.flush();
                writer.close();
            }
        }
    }

    /**
     * 读取剩下的srt的文档
     *
     * @param filepath
     * @param buffer
     * @return
     * @throws FileNotFoundException
     */
    public Map<String, Object> getBalSrtFileByLine(String filepath, StringBuffer buffer, int lastIndex, String lastTimeVersion) throws FileNotFoundException {
        InputStream inputstream = null;
        BufferedReader reader = null;
        String temp = "";
        Map<String, Object> result = new HashMap<>();
        try {
            inputstream = new FileInputStream(new File(filepath));
            reader = new BufferedReader(new InputStreamReader(inputstream));
            List<String> content = new ArrayList<>();
            String starttime = lastTimeVersion;
            int index = lastIndex;
            while ((temp = reader.readLine()) != null) {
                //判断是最后一行的时间戳
                try {
                    int begin = Integer.valueOf(temp);
                    continue;
                } catch (Exception e) {
                    //不做处理
                }
                int interval = 0;//前后时间间隔
                if (temp.contains("-->")) {
                    String[] times = temp.split("-->");
                    String firstTime = times[0].trim().replace(",", "");//开始时间串
                    String secondTime = times[1].trim().replace(",", "");//结束时间串
                    //统计开始时间
                    String[] firstTimeList = firstTime.split(":");
                    int firstTotalIntervalTime = Integer.valueOf(firstTimeList[0]) * 60 * 60 * 1000 + Integer.valueOf(firstTimeList[1]) * 60 * 1000 + Integer.valueOf(firstTimeList[2]);//firstLast.intValue();
                    String[] secondTimeList = secondTime.split(":");
                    int secondTotalIntervalTime = Integer.valueOf(secondTimeList[0]) * 60 * 60 * 1000 + Integer.valueOf(secondTimeList[1]) * 60 * 1000 + Integer.valueOf(secondTimeList[2]);//secondLast.intValue();
                    interval = secondTotalIntervalTime - firstTotalIntervalTime;

                    String curStarttimeTime = starttime.replace(",", "");//开始时间串
                    //统计开始时间
                    String[] startTimeList = curStarttimeTime.split(":");
//                    Double startLast= (Double.valueOf(startTimeList[2]))*1000;
                    int startTotalIntervalTime = Integer.valueOf(startTimeList[0].trim()) * 60 * 60 * 1000 + Integer.valueOf(startTimeList[1].trim()) * 60 * 1000 + Integer.valueOf(startTimeList[2]);//startLast.intValue();

                    buffer.append(index + 1);
                    buffer.append("\n");
                    buffer.append(starttime);
                    buffer.append(" --> ");
                    int localtime = startTotalIntervalTime + interval;
                    int time2 = 0;
                    int time3 = 0;
                    int time1 = 0;
                    if (localtime >= 60 * 60 * 1000) {
                        time1 = localtime / (60 * 60 * 1000);
                        int balanceTime = localtime % (60 * 60 * 1000);
                        if (balanceTime >= 60000) {
                            time2 = localtime / 60000;
                            time3 = localtime % 60000;
                        } else {
                            time3 = localtime;
                        }
                    } else if (localtime >= 60000) {
                        time2 = localtime / 60000;
                        time3 = localtime % 60000;
                    } else {
                        time3 = localtime;
                    }
                    StringBuffer time3Buffer = new StringBuffer();
                    buffer.append("0" + time1 + ":");
                    time3Buffer.append("0" + time1 + ":");
                    if (time2 < 10) {
                        buffer.append("0" + time2);
                        time3Buffer.append("0" + time2);
                    } else {
                        buffer.append(time2);
                        time3Buffer.append(time2);
                    }
                    buffer.append(":");
                    time3Buffer.append(":");
                    String time3Str = time3 + "";
                    if (time3 < 10000 && time3 >= 1000) {
                        buffer.append("0" + time3Str.substring(0, 1) + "," + time3Str.substring(1));
                        time3Buffer.append("0" + time3Str.substring(0, 1) + "," + time3Str.substring(1));
                    } else if (time3 < 1000) {
                        buffer.append("00" + "," + time3Str);
                        time3Buffer.append("00" + "," + time3Str);
                    } else {
                        buffer.append(time3Str.substring(0, 2) + "," + time3Str.substring(2));
                        time3Buffer.append(time3Str.substring(0, 2) + "," + time3Str.substring(2));
                    }
                    buffer.append("\n");
                    starttime = time3Buffer.toString();
                    content.add(time3Buffer.toString());
                    result.put("lastIndex", index + 1);
                    result.put("lastTimeVersion", time3Buffer.toString());
                    index++;
                } else {
                    buffer.append(temp.trim());
                    buffer.append("\n");
                }

            }
//            for (int i = content.size() - 1; i >= 0; i--) {
//                if (content.get(i).contains("-->")) {
//                    int innerlastIndex = Integer.valueOf(content.get(i - 1).trim());
//                    String innerlastTimeVersion = (content.get(i).split("-->"))[1];
//                    result.put("lastIndex", innerlastIndex);
//                    result.put("lastTimeVersion", innerlastTimeVersion);
//                    break;
//                }
//            }
        } catch (Exception e) {
            System.out.println(e.toString());
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (inputstream != null) {
                try {
                    inputstream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    //读取第一个srt的文档
    public Map<String, Object> getSrtFileByLine(String filepath, StringBuffer buffer) throws FileNotFoundException {
        InputStream inputstream = null;
        BufferedReader reader = null;
        String temp = "";
        Map<String, Object> result = new HashMap<>();
        try {
            inputstream = new FileInputStream(new File(filepath));
            reader = new BufferedReader(new InputStreamReader(inputstream));
            List<String> content = new ArrayList<>();
            while ((temp = reader.readLine()) != null) {
                buffer.append(temp.trim());
                buffer.append("\n");
                //判断是最后一行的时间戳
                content.add(temp.trim());
            }
            for (int i = content.size() - 1; i >= 0; i--) {
                if (content.get(i).contains("-->")) {
                    int lastIndex = Integer.valueOf(content.get(i - 1).trim());
                    String lastTimeVersion = (content.get(i).split("-->"))[1];
                    result.put("lastIndex", lastIndex);
                    result.put("lastTimeVersion", lastTimeVersion.trim());
                    break;
                }
            }
            System.out.println(content.toString());
        } catch (Exception e) {
            System.out.println(e.toString());
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (inputstream != null) {
                try {
                    inputstream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

}
